{
 "cells": [
  {
   "cell_type": "code",
   "id": "initial_id",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "from typing import Tuple, List\n",
    "\n",
    "import click\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "import math\n",
    "from src.algorithms.clustering_algorithm.duration_limited_clusterer import batch_duration_limited_cluster, \\\n",
    "    DurationLimitedClusterer, duration_limited_cluster\n",
    "from src.algorithms.clustering_algorithm.size_limited_clusterer import SizeLimitedClusterer\n",
    "from src.services.direct_router.direct_router_rpc_server import DirectRouterRpcServer\n",
    "from src.services.multi_stop_router.multi_stop_router_rpc_server import MultiStopRouterRPCServer\n",
    "from src.utils.utils import random_choice\n",
    "from src.data.data_loader import load_map_data, get_bbox\n",
    "from src.algorithms.result_visualizer.result_visualizer import ResultVisualizer\n",
    "from src.algorithms.data_preprocessor.preprocess_data import preprocess_data\n",
    "\n",
    "# 配置中文字体支持\n",
    "import matplotlib.pyplot as plt\n",
    "import matplotlib\n",
    "\n",
    "# 设置中文字体，解决中文显示问题\n",
    "plt.rcParams['font.sans-serif'] = ['Arial Unicode MS', 'SimHei', 'DejaVu Sans']\n",
    "plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "def batch_cluster(bbox: Tuple[float, float, float, float], points: np.ndarray,\n",
    "                  warehouse_coords: List[Tuple[float, float]], per_delivery_duration: int,\n",
    "                  work_duration: int) -> np.ndarray:\n",
    "    \"\"\" 拆分区域，并行聚类 \"\"\"\n",
    "    click.echo(f\"运单数量: {len(points)}，进行区域拆分并行聚类...\")\n",
    "    mini_cluster_size = 10  # 每个微簇的点数\n",
    "    zone_size = min(1500, round(points.shape[0] / 3))  # 每个区域的点数\n",
    "    # 1.聚类微簇\n",
    "    click.echo(\"正在进行微簇聚类...\")\n",
    "    num_clusters = math.ceil(points.shape[0] / mini_cluster_size)\n",
    "    centroids = random_choice(points, num_clusters)\n",
    "    clusterer = SizeLimitedClusterer(points, mini_cluster_size)\n",
    "    mini_cluster_labels, _ = clusterer.clustering(centroids, step=1, max_iter=30)\n",
    "\n",
    "    # 2.每个微簇取一个点\n",
    "    mini_cluster_points = []\n",
    "    for l in np.unique(mini_cluster_labels):\n",
    "        ps = points[mini_cluster_labels == l]\n",
    "        mini_cluster_center = np.mean(points[mini_cluster_labels == 0], axis=0)\n",
    "        center_index = np.argmin(np.linalg.norm(mini_cluster_center - ps, axis=1))\n",
    "        mini_cluster_points.append(ps[center_index])\n",
    "    mini_cluster_points = np.array(mini_cluster_points)\n",
    "\n",
    "    # 3.区域聚类\n",
    "    click.echo(\"正在进行区域聚类...\")\n",
    "    num_clusters = math.ceil(mini_cluster_points.shape[0] / round(zone_size / mini_cluster_size))\n",
    "    centroids = random_choice(mini_cluster_points, num_clusters)\n",
    "    clusterer = SizeLimitedClusterer(mini_cluster_points, round(zone_size / mini_cluster_size))\n",
    "    mini_cluster_to_zone_labels, _ = clusterer.clustering(centroids, step=2, max_iter=30)\n",
    "\n",
    "    # 4.将微簇标签映射到区域标签，得到 zone_labels\n",
    "    un_mini_cluster_labels = np.unique(mini_cluster_labels)\n",
    "    zone_labels = np.full(len(mini_cluster_labels), -1)\n",
    "    for l in mini_cluster_to_zone_labels:\n",
    "        zone_labels[np.isin(mini_cluster_labels, un_mini_cluster_labels[mini_cluster_to_zone_labels == l])] = l\n",
    "\n",
    "    # 5.批量聚类\n",
    "    click.echo(f\"正在从 {len(np.unique(zone_labels))} 个区域中进行批量聚类...\")\n",
    "    result_labels = batch_duration_limited_cluster(bbox, points, zone_labels, warehouse_coords, per_delivery_duration,\n",
    "                                                   work_duration)\n",
    "    return result_labels"
   ],
   "id": "7370023f84afc000",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "orders_excel = 'order_data/SD_1296.xlsx'\n",
    "# orders_excel = 'order_data/SD_3363.xlsx'\n",
    "warehouse_coords = [(32.8663918101245, -117.21455873652934)]  # SD仓库坐标 - 支持多仓库列表格式\n",
    "# orders_excel = 'order_data/LA_13112.xlsx'\n",
    "# warehouse_coords = [(33.95493075987019, -118.37453638829928)]  # LA_AD仓库坐标 - 支持多仓库列表格式\n",
    "# 多仓库示例：\n",
    "# warehouse_coords = [(32.8663918101245, -117.21455873652934), (33.95493075987019, -118.37453638829928)]  # SD + LA仓库\n",
    "# orders_excel = 'order_data/LA_SD_16475.xlsx'\n",
    "per_delivery_duration = 5 * 60  # 默认值设置为 300 秒 (5分钟)\n",
    "work_duration = 10 * 60 * 60  # 默认值设置为 36000 秒 (10小时)"
   ],
   "id": "efb17af91da6e81e",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "# 读取订单数据\n",
    "click.echo(f\"正在读取订单数据...\")\n",
    "df = pd.read_excel(orders_excel, dtype={'business_code': str, 'latitude': float, 'longitude': float})\n",
    "# 提取经纬度坐标\n",
    "points = df[['latitude', 'longitude']].values\n",
    "click.echo(f\"共读取 {len(points)} 个运单。\")"
   ],
   "id": "f7096935f9985951",
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "id": "f86c2b36f7c17e73",
   "metadata": {},
   "source": "# 根据仓库坐标获取边界框\nbbox = get_bbox(warehouse_coords, points)\n# 读取地图数据（假设此函数使用仓库坐标）\nclick.echo(f\"加载地图数据...\")\nload_map_data(bbox)",
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "id": "7b6ebfb440a53679",
   "metadata": {},
   "source": [
    "# 启动DirectRouterRpcServer\n",
    "direct_router_rpc_server = DirectRouterRpcServer()\n",
    "direct_router_rpc_server.start_server(bbox=bbox)\n",
    "\n",
    "# 启动MultiStopRouterRPCServer\n",
    "multi_stop_router_rpc_server = MultiStopRouterRPCServer()\n",
    "multi_stop_router_rpc_server.start_server(bbox=bbox)"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "# 预处理数据\n",
    "click.echo(\"正在预处理数据...\")\n",
    "preprocess_data(points, 50)"
   ],
   "id": "9b0386b0f4f65ea3",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "if points.shape[0] < 2000:\n",
    "    click.echo(f\"运单数量: {len(points)}，进行单进程聚类...\")\n",
    "    result_labels = duration_limited_cluster(points, warehouse_coords, per_delivery_duration, work_duration)\n",
    "else:\n",
    "    click.echo(f\"运单数量: {len(points)}，进行并行聚类...\")\n",
    "    result_labels = batch_cluster(bbox, points, warehouse_coords, per_delivery_duration, work_duration)"
   ],
   "id": "46e1a974b0d6930d",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": "result_visualizer = ResultVisualizer(points, result_labels, warehouse_coords, per_delivery_duration)",
   "id": "436a0d9b6cf6ae0",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": "result_visualizer.draw_map()",
   "id": "36dd51a81ed7a1d2",
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "id": "28058f90",
   "metadata": {},
   "source": "# result_visualizer.export(df)",
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "id": "9fb4007b",
   "metadata": {},
   "source": [
    "df2 = result_visualizer.statistical()"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "id": "fed0f047",
   "metadata": {},
   "source": [
    "# 柱状图展示df2的total_working_time字段分布情况\n",
    "df2['total_working_time'].plot(kind='bar', title='Total Working Time Distribution')"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": "df2['total_working_time'].sum()",
   "id": "833e2eb23f4c3be5",
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "id": "16924d87",
   "metadata": {},
   "source": [
    "df2['total_working_time'].describe()"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "click.echo(\"正在使用MST算法优化聚类结果...\")\n",
    "from src.algorithms.clustering_algorithm.cluster_refiner import refine_clusters\n",
    "optimized_labels = refine_clusters(points, result_labels, min_cluster_size=3, small_group_threshold=0.1)"
   ],
   "id": "881d6d82cd93c57d",
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "id": "8bca1bee",
   "metadata": {},
   "source": [
    "# 统计优化效果\n",
    "num_changes = np.sum(result_labels != optimized_labels)\n",
    "click.echo(f\"聚类优化完成，共移动了 {num_changes} 个运单\")\n",
    "\n",
    "# 创建优化后的结果可视化器\n",
    "optimized_visualizer = ResultVisualizer(points, optimized_labels, warehouse_coords, per_delivery_duration)\n",
    "\n",
    "# 展示优化前后对比\n",
    "click.echo(\"正在生成优化前的统计数据...\")\n",
    "original_stats = result_visualizer.statistical()\n",
    "original_total_time = original_stats['total_working_time'].sum()\n",
    "\n",
    "click.echo(\"正在生成优化后的统计数据...\")\n",
    "optimized_stats = optimized_visualizer.statistical()\n",
    "optimized_total_time = optimized_stats['total_working_time'].sum()\n",
    "\n",
    "# 计算优化收益\n",
    "time_improvement = original_total_time - optimized_total_time\n",
    "improvement_percentage = (time_improvement / original_total_time) * 100\n",
    "\n",
    "click.echo(f\"优化前总工作时间: {original_total_time:.2f} 小时\")\n",
    "click.echo(f\"优化后总工作时间: {optimized_total_time:.2f} 小时\")\n",
    "click.echo(f\"时间节省: {time_improvement:.2f} 小时 ({improvement_percentage:.2f}%)\")\n",
    "\n",
    "# 绘制优化后的地图\n",
    "click.echo(\"正在绘制优化后的聚类地图...\")\n",
    "optimized_visualizer.draw_map()"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "id": "h2gqrsyj9z6",
   "source": "optimized_stats['total_working_time'].plot(kind='bar', title='Total Working Time Distribution')",
   "metadata": {},
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": "original_stats['total_working_time'].describe()",
   "id": "5b2fd0ce346dba89",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": "optimized_stats['total_working_time'].describe()",
   "id": "985bea32a73bf954",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "direct_router_rpc_server.stop_server()\n",
    "multi_stop_router_rpc_server.stop_server()"
   ],
   "id": "e9a936745f24ae67",
   "outputs": [],
   "execution_count": null
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "distribution-planning-fYNSEMLE-py3.11",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
